// -----------------------------------------------------------------------
//  <copyright file="DaprPlusContext.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2022 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://www.dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2022-11-25 19:58</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Authorization;
using DaprPlus.Properties;

using Microsoft.Extensions.DependencyModel;


namespace DaprPlus.Builder;

/// <summary>
/// DaprPlus上下文
/// </summary>
public class DaprPlusContext
{
    private readonly ConcurrentDictionary<Assembly, Type[]> _assemblyTypesMap = new();
    private Type[] _allTypes = Type.EmptyTypes;
    private readonly ConcurrentDictionary<string, Type[]> _typeFinderCache = new();

    /// <summary>
    /// 获取 服务名称
    /// </summary>
    public string ServiceName { get; private set; } = null!;

    /// <summary>
    /// 获取所有模块信息
    /// </summary>
    public ModuleBase[] Modules { get; private set; } = [];

    /// <summary>
    /// 初始化
    /// </summary>
    internal void Init(ModuleBase[] modules, IConfiguration configuration)
    {
        Modules = modules;
        ServiceName = configuration["ServiceName"] ?? throw new DaprPlusException(ResDaprPlus.String_Set_ServiceName);
        LoadAllTypes();
    }

    private void LoadAllTypes()
    {
        var allTypes = new List<Type>();
        if (DependencyContext.Default == null)
        {
            return;
        }

        var libraries = DependencyContext.Default.RuntimeLibraries.Where(m => m.Type == "project").ToList();
        foreach (var library in libraries)
        {
            var assembly = Assembly.Load(new AssemblyName(library.Name));
            if (_assemblyTypesMap.ContainsKey(assembly))
            {
                continue;
            }

            var types = assembly.GetTypes();
            _assemblyTypesMap[assembly] = types;
            allTypes.AddRange(types);
        }

        _allTypes = [.. allTypes];
    }

    /// <summary>
    /// 查找指定条件的类型
    /// </summary>
    public Type[] FindTypes(Func<Type, bool> expression)
    {
        // 对于委托参数，我们可以使用委托的目标方法和目标对象作为缓存键的一部分
        // 注意：这种方法对于匿名方法和Lambda表达式可能不总是有效，因为每次创建的委托可能不同
        var methodInfo = expression.Method;
        var target = expression.Target;

        string cacheKey;
        // 如果是静态方法或没有目标对象，我们可以使用方法信息作为键
        if (target == null)
        {
            cacheKey = $"Method_{methodInfo.DeclaringType?.FullName}_{methodInfo.Name}";
            return _typeFinderCache.GetOrAdd(cacheKey, _ => [.. _allTypes.Where(expression)]);
        }

        // 对于实例方法，我们可以尝试使用目标对象的哈希码和方法信息组合作为键
        // 注意：这对于每次创建的新Lambda表达式仍然不会命中缓存
        var targetHashCode = target.GetHashCode();
        cacheKey = $"Method_{targetHashCode}_{methodInfo.DeclaringType?.FullName}_{methodInfo.Name}";
        return _typeFinderCache.GetOrAdd(cacheKey, _ => [.. _allTypes.Where(expression)]);
    }

    /// <summary>
    /// 查找指定基类的实现类型
    /// </summary>
    /// <typeparam name="TBaseType">基类类型</typeparam>
    /// <returns></returns>
    public Type[] FindTypesByBase<TBaseType>()
    {
        var baseType = typeof(TBaseType);
        return FindTypesByBase(baseType);
    }

    /// <summary>
    /// 查找指定基类的实现类型
    /// </summary>
    /// <param name="baseType">基类类型</param>
    /// <returns></returns>
    public Type[] FindTypesByBase(Type baseType)
    {
        var cacheKey = $"Base_{baseType.FullName}";
        return _typeFinderCache.GetOrAdd(cacheKey, _ => [.. _allTypes.Where(type => type.IsDeriveClassFrom(baseType)).Distinct()]);
    }

    /// <summary>
    /// 查找指定Attribute特性的实现类型
    /// </summary>
    public Type[] FindTypesByAttribute<TAttribute>(bool inherit = true)
    {
        var attributeType = typeof(TAttribute);
        return FindTypesByAttribute(attributeType, inherit);
    }

    /// <summary>
    /// 查找指定Attribute特性的实现类型
    /// </summary>
    public Type[] FindTypesByAttribute(Type attributeType, bool inherit = true)
    {
        var cacheKey = $"Attr_{attributeType.FullName}_{inherit}";
        return _typeFinderCache.GetOrAdd(cacheKey, _ => [.. _allTypes.Where(type => type.IsDefined(attributeType, inherit)).Distinct()]);
    }
}
